package sound

import collection.mutable.{ArrayBuffer, ListBuffer}
import javax.sound.sampled.{AudioFormat, AudioSystem, SourceDataLine}
import java.io._
import audio.cords.{SimplestChart, Mike, Filter}
import org.jfree.ui.RefineryUtilities
import org.jfree.chart.{ChartFactory, JFreeChart}
import org.jfree.chart.renderer.xy.XYSplineRenderer
import java.awt.{Shape, Graphics2D}
import org.jfree.chart.entity.EntityCollection
import org.jfree.chart.plot.{CrosshairState, XYPlot, FastScatterPlot, PlotOrientation}
import java.awt.geom.Rectangle2D
import org.jfree.chart.axis.{ValueAxis, NumberAxis}
import org.jfree.data.xy.{XYDataset, XYSeries, XYSeriesCollection}
import audio.DFT
import flanagan.interpolation.CubicSpline
import cern.jet.random.engine.MersenneTwister64

/**
 * Created by IntelliJ IDEA.
 * User: дом
 * Date: 26 черв 2010
 * Time: 22:34:03
 * To change this template use File | Settings | File Templates.
 */

object SoundExplore extends LocalSimilar{
  def main(args: Array[String]) = {
    paintSound()
    playShGen()
    //play()
    //shRecognize()

    val data = soundFileData(soundFileStream)
    printf("sound samples %d,  seconds %f %n", data.size, data.size.toDouble / rate)
    //printVolumeStats(soundFileData)
    printCorr()
    //printLocalCorr()
  }

  def printMaxDeltas(sound: Array[Short]){
    var maxPrev=0
    for(i<-1 until sound.size-1){
      if(sound(i-1)<=sound(i) && sound(i)>=sound(i+1)){
        if(maxPrev!=0){
          println (i+" "+(i-maxPrev)+" "+rate.toDouble/(i-maxPrev)+"Hz")
        }
        maxPrev=i
      }
    }
  }

  def findMaximums(sound:Array[Short]):List[(Int,Short)] = {
    val ret = new ListBuffer[(Int,Short)]
    for(i<-1 until sound.size-1){
      if(sound(i-1)<=sound(i) && sound(i)>=sound(i+1) && sound(i)>0){
        ret += Pair(i,sound(i))
      }
    }
    ret.toList
  }

  def printZeroCrossings(sound: Array[Short]){
    var prevZero=0d
    def next(p:Double){
      if(prevZero!=0){
        println(p+" "+(p-prevZero)+" "+rate/(p-prevZero)/2+"Hz")
      }
      prevZero=p;
    }
    for(i<-1 until sound.size-1){
      if(sound(i)==0){
        next(i)
      }else if( sound(i).toLong*sound(i+1)<0 ){
        next( i+ sound(i).toDouble.abs/(sound(i+1).toDouble.abs+sound(i).abs) )
      }
    }
  }

  def graphData(sound:Array[Short]):XYSeriesCollection={
    val data: XYSeriesCollection = new XYSeriesCollection
    val series: XYSeries = new XYSeries("Series " + 1)
    sound.zipWithIndex.foreach{ case (v,i) => series.add(i, v) }
    val data2 = new Array[Array[Float]](2)
    data2(0) = (0 until sound.size).map(_.toFloat).toArray
    data2(1) = sound.map(_.toFloat).toArray
    data.addSeries(series)
    data
  }

  def graphData(d:List[(Int,Short)]):XYSeriesCollection={
    val data: XYSeriesCollection = new XYSeriesCollection
    val series: XYSeries = new XYSeries("Series " + 1)
    d.foreach(a => series.add(a._1, a._2))
    data.addSeries(series)
    data
  }

  val rnd = new java.util.Random
  class Distorter(size:Int, step:Int, strength:Double){
    val d = for( i<-0.to(size+step, step) ) yield (i,i+rnd.nextGaussian*strength)
    val spline = new CubicSpline( d.map(_._1.toDouble).toArray, d.map(_._2.toDouble).toArray )
    def xdist(x:Double) = spline.interpolate(x)
  }

  def shGenerate(orig:Array[Short])={
    val mx = findMaximums(orig)
    val mxVals = mx.map(_._2.toDouble).toArray
    val mxValsMx = mxVals.max
    val spline = new CubicSpline( mx.map(_._1.toDouble).toArray, mxVals )
    val ret = new Array[Short](orig.size)
    
    case class Puff(i:Double,l:Double,totLen:Double){
      def has(idx:Int) = idx>=i && idx < i+totLen
      //def koef(idx:Int) = if( idx-i<=l ) idx-i else Math.exp( -(idx-i-l)/l )
      def value(idx:Int) = Math.sin( (idx-i)*2*Math.Pi/l )//*koef(idx)
    }


    var rnd64 = new MersenneTwister64(new java.util.Date)
    def nextNoise01 = {
      var s: Double = 0
        var j: Int = 0
        while (j < 12) {
          s += rnd64.nextDouble
          j += 1
        }
      s / 12
    }



  //var puffs = List[Puff]()
    var puff = Puff(0,0,0)
    val dist = new Distorter(orig.size, 400, 350)
    for( i<-0 until orig.size ){
      val form = spline.interpolate( spline.getXmin.max(i).min(spline.getXmax) )
      //val sinVari = (0.99995+0.0001*(1-form.abs/mxValsMx))
      //val sinVari = 1//0.998+0.004*Math.sin(i/40).abs
      //ret(i) = (form * Math.sin(i*Math.Pi*20/64 * sinVari)).round.toShort

//      puffs = puffs.filter(_.has(i))
//      if(puffs.filter(_.has(i+1)).isEmpty){
//        puffs ::= Puff(i, 6.8+rnd.nextGaussian, 20 )
//      }
//      val v = puffs.map(_.value(i)).sum/puffs.size

      if( puff has i ){
      }else{
        val dlen = 6.8+rnd.nextGaussian
        puff = Puff(puff.i+puff.totLen, dlen, dlen)
      }
      //val v = puff value i
      //val v = nextNoise01
      //val v = Math.sin(dist.xdist(i)*Math.Pi*20/64).round.toShort
      //ret(i) = (form /*25000*/ * v).round.toShort

      val koef = 17000/form
      //ret(i) = (orig(i)*Math.pow(koef,0.5)).round.toShort
      ret(i) = orig(i).*(1+0.7*Math.sin(dist.xdist(i)*2*Math.Pi/200)).round.toShort
    }
    ret
  }

  def playShGen(){
    val sound = soundFileData(soundFileStream)
    val filtered = shFilter(sound)
    val genSh = shGenerate(filtered)

    val a = soundToBytes(genSh)

      var sampleRate: Int = 11025
      println("seconds: "+(a.size.toDouble/2/*bytes*//sampleRate))
      var line: SourceDataLine = AudioSystem.getSourceDataLine(new AudioFormat(sampleRate, 16, 1, true, true))
      line.open()
      line.start()
      line.write(a, 0, a.size)
  }
  
  def paintSound() = {
    val start = 29000 // shsh
    //val start = 320000 // ssss
    //val test = lowPass(diod(shFilter(soundFileData(soundFileStream)))).slice(start, start+1000)
    //val test = shFilter( filter(fewFreqKoef)(soundFileData(soundFileStream)) ).slice(start, start+1000)
    val test = shFilter( soundFileData(soundFileStream) ).slice(start, start+1000)
    //printMaxDeltas(test)
    printZeroCrossings(test)
    /*
    val dft = new DFT
    dft.forward(test.slice(0,512).map(_.toDouble))
    for(i<-0 until dft.getMagnitudes.size){
      if(dft.getMagnitudes()(i)>100000){
        println(i+" "+dft.getMagnitudes()(i)/1000)
      }
    }
    */
    //val test = soundFileData(soundFileStream).slice(28000, 28000+1000) // shsh

    //val data = graphData(test)
    //val data = graphData(findMaximums(test))
    val data = graphData(shGenerate(test))

    var demo: SimplestChart = new SimplestChart(data){
      override def createChart(data: XYSeriesCollection) = {
        var chart: JFreeChart = ChartFactory.createScatterPlot(null, "X", "Y", data, PlotOrientation.VERTICAL, true, false, false)
        val renderer: XYSplineRenderer = new XYSplineRenderer{
          //override def drawFirstPassShape(g2: Graphics2D, pass: Int, series: Int, item: Int, shape: Shape) = {}

          override def drawSecondaryPass(g2: Graphics2D, plot: XYPlot, dataset: XYDataset, pass: Int, series: Int, item: Int, domainAxis: ValueAxis, dataArea: Rectangle2D,
                                         rangeAxis: ValueAxis, crosshairState: CrosshairState, entities: EntityCollection) = {}
        }
        chart.getXYPlot.setRenderer(renderer)
        //val plot = new FastScatterPlot(data2, new NumberAxis("X"), new NumberAxis("Y"));
        //val chart = new JFreeChart("Fast Scatter Plot", plot);
        chart
      }
    }
    RefineryUtilities.centerFrameOnScreen(demo)
    demo.setVisible(true)
  }

}